import uvicorn
from fastapi import FastAPI, Header
from pydantic import BaseModel
from jose import jwt

from typing import Optional
import pymysql
from dbutils.pooled_db import PooledDB
# 导入日期模块，用来验证token过期时间
from datetime import datetime, timedelta
# 导入token校验的两种异常，一种是token超时异常，一种是token错误异常
from jose.exceptions import ExpiredSignatureError, JWTError

# 定义秘钥，很重要，请勿泄露
SECRET_KEY = 'baidaiyiju'


# 生成token的代码，token过期时长定义为默认参数，单位为秒
def create_token(seconds=60, username='xx'):
    # 定义token过期时间，为当前utc时间加上token有效期
    expire = datetime.now().timestamp() + seconds
    # 此处即为生成token的payload，sub自定义（可选用户名），uid自定义（可选设备id等）
    # to_endoe可以是一个空的字典，但是不建议这样做
    to_encode = {"exp": expire, "sub": SECRET_KEY, "uid": username}
    # 使用jwt生成token，token是根据payload和秘钥共同生成。
    jwt_token = jwt.encode(claims=to_encode, key=SECRET_KEY)
    return jwt_token


def judge_token(token):
    # 尝试解密，解密成功后，秘钥，payload，有效时长校验通过，则会返回payload
    try:
        payload = jwt.decode(token, SECRET_KEY)
        username = payload.get('uid')

        now = datetime.now().timestamp()
        exp = payload.get("exp")
        if now > exp :
            return 2

        # 从连接池获取连接
        conn = pool.connection()
        # 获取游标
        cursor = conn.cursor()
        # 执行sql
        sql = 'select * from wss_user_account where username = %s'
        cursor.execute(sql, username)

        results = cursor.fetchall()
        if results:
            print('token有效，用户存在')
            return 0
        else:
            print('解析token后用户不存在！')
            return 1

    # 否则，则捕获异常，进行提示
    except ExpiredSignatureError as e:
        print('token过期了！')
        return 2
    except JWTError as e:
        print('token验证失败！')
        return 3


# 创建FastAPI实例
app = FastAPI()

# 定义连接参数
pool = PooledDB(
    # 数据库驱动模块
    creator=pymysql,
    # 最大连接数
    maxconnections=6,
    # 最少的空闲连接数，如果空闲连接数小于这个数，pool会创建一个新的连接
    mincached=2,
    # 最大的空闲连接数，如果空闲连接数大于这个数，pool会关闭空闲连接
    maxcached=5,
    # 当连接数达到最大的连接数时，在请求连接的时候，如果这个值是True，请求连接的程序会一直等待，直到当前连接数小于最大连接数，如果这个值是False，会报错
    blocking=True,
    # 主机
    host='localhost',
    # 端口号
    port=3306,
    # 用户名
    user='root',
    # 密码
    password='BaiguoqiangMysql533',
    # 数据库
    database='test1',
    # 编码
    charset='utf8mb4',
    # 设置数据以字典的形式返回
    cursorclass=pymysql.cursors.DictCursor
)


# 定义数据模型
class User(BaseModel):
    username: str
    password: str


# 基础路由
@app.get("/api/hello")
def read_root():
    return {"Hello": "World"}


@app.post("/api/login")
def login(user_req: User):
    try:
        # 从连接池获取连接
        conn = pool.connection()
        # 获取游标
        cursor = conn.cursor()
        # 执行sql
        sql = 'select * from wss_user_account where username = %s and password = %s'
        cursor.execute(sql, (user_req.username, user_req.password))

        results = cursor.fetchall()
        if results:
            one_user = results[0]
            user_username = one_user['username']
            token = create_token(60 * 60 * 24, user_username)
            # token = create_token(5, user_username)
            # token = create_token(20, user_username)
            return {"code": 10000, "msg": "ok", "token": token}
        else:
            return {"code": 10003, "msg": "用户名或密码错误"}
    except:
        return {"code": 500, "msg": "服务器端错误"}


@app.get("/api/goods/list")
def create_item(Authorization: str = Header(None)):
    try:
        if Authorization is None:
            return {"code": 10004, "msg": "token不存在,请您登录"}
        else:
            judge_res = judge_token(Authorization)
            if judge_res == 0:
                # 从连接池获取连接
                conn = pool.connection()
                # 获取游标
                cursor = conn.cursor()
                # 执行sql
                sql = 'select * from es_goods1'
                cursor.execute(sql)
                results = cursor.fetchall()
                return {"code": 10000, "msg": "ok", "data" : results}

            elif judge_res == 1:
                return {"code": 10005, "msg": "token用户不存在"}

            elif judge_res == 2:
                return {"code": 10006, "msg": "token已过期"}
            elif judge_res == 3:
                return {"code": 10007, "msg": "token无效"}

            return {"code": 10008, "msg": "未知异常"}
    except:
        return {"code": 500, "msg": "服务器端错误"}


if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8123)
